

use std::{fs::{create_dir_all, remove_dir_all, rename}, path::PathBuf};

use clap::Args;
use serde::{Deserialize, Serialize};
use wtask_base::{app::{get_wtask_app_dir, get_wtask_data_dir}, cmd::{file_cp, file_zip}, error::{WError, WResult}, export::Local, logs::TIME_FORMAT_TIMELINE, tcp::{WTcpStream, stream_read_data, stream_write_data}};

use crate::{basic::WPath, file::{download::AppFileDownload, utils::FileUDArgs}, task::utils::meta::WTASK_SOURCE_FILE, utils::{AppTrait, StreamStatus}};




#[derive(Debug, Clone, Serialize, Deserialize, Args, Default)]
pub struct AppTaskDownload {
    /// 指定预定义任务下载，默认不指定为下载所有
    #[arg(long, requires = "version")]
    pub app: Option<String>,

    /// 指定预定义任务的版本
    #[arg(long, requires = "app")]
    pub version: Option<String>,

    /// 指定下载的结果文件夹，默认当前文件夹
    #[arg(long)]
    pub dir: Option<PathBuf>,
}


impl AppTrait for AppTaskDownload {
    type Status = ();
    type Res = WPath;

    async fn client_handle<M: FnMut(bool, String) + Send + Sync>(self, stream: &WTcpStream, key: &str, mut msg_func: M, status: Option<Self::Status>) -> WResult<Self::Res> {
        msg_func(false, "Task zipping ...".to_owned());
        let mut file_download = {
            let file_remote: WPath = stream_read_data(stream.0.as_ref(), key).await?;
            let t = FileUDArgs::new(file_remote.into());
            AppFileDownload::from(t)
        };
        file_download.dir = self.dir;
        msg_func(false, format!("Task ziped: {:?}", file_download.data.file));
        let file_local = file_download.client_handle(stream, key, &mut msg_func, status).await?;
        let file_local: WPath = file_local.try_into()?;
        let file_result = if let Some(app) = self.app && let Some(version) = self.version {
            let file_raw: PathBuf = file_local.into();
            let dir = file_raw.parent().ok_or(WError::Empty)?;
            let file_res = dir.join(format!("{}.{}.zip", app, version));
            msg_func(false, "Task Archive rename ...".to_owned());
            let r = WPath::from(&file_res);
            rename(file_raw, file_res)?;
            r
        } else {
            file_local
        };
        msg_func(false, format!("Task Archive Saved: {}", file_result));
        Ok(file_result)
    }

    async fn server_handle(self, server_token: &str, session_token: &str, stream_session: &WTcpStream, stream_status: StreamStatus) -> WResult<()> {
        let file_result = if let Some(app) = self.app && let Some(version) = self.version {
            get_wtask_app_dir(&app, &version)?.join(WTASK_SOURCE_FILE)
        } else {
            // all task
            let dir_file = get_wtask_data_dir("file", true)?;
            let dir_task = get_wtask_data_dir("task", false)?;
            let dir_name = format!("wTask.TaskArch.{}", Local::now().format(TIME_FORMAT_TIMELINE));
            let dir_result = dir_file.join(&dir_name);
            let file_result = dir_file.join(format!("{dir_name}.zip"));

            create_dir_all(&dir_result)?;

            // cp
            for p in dir_task.read_dir().unwrap() {
                let p = p.unwrap().path();
                let n = format!("{}.zip", p.file_name().unwrap().to_str().unwrap());
                let f2 = dir_result.join(n);
                let f = p.join(WTASK_SOURCE_FILE);
                file_cp(&f, &f2)?;
                
            }
            // zip
            file_zip(&dir_result, &file_result)?;

            // rm
            let _ = remove_dir_all(&dir_result);

            file_result
        };


        // tell client
        stream_write_data(&WPath::from(&file_result), stream_session.1.as_ref(), session_token).await?;

        // start download
        let t = FileUDArgs::new(file_result);
        let file_download = AppFileDownload::from(t);
        file_download.server_handle(server_token, session_token, stream_session, stream_status).await
    }
}
